package com._21cn.framework.utils;

import com._21cn.framework.utils.emulehash.IMD4;
import com._21cn.framework.utils.emulehash.MD4Factory;

import java.io.*;
import java.util.*;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 文件类工具
 * 
 */
public final class FileUtil {
	/**
	 * 文件的元信息，如名称，类型，路径，内容hash值等
	 * 
	 */
	public static class FileInfo {
		private File obj;

		private String fileName = "";

		private String name = "";

		private String ext = "";

		private String path = "";

		private String hashCode = null;

		public FileInfo(File f) {
			this.obj = f;
			this.fileName = f.getName();
			this.path = f.getPath();
			int endIndex = fileName.lastIndexOf( "." );
			if( endIndex > 0 ) {
				this.name = fileName.substring( 0, endIndex );
				this.ext = fileName.substring( endIndex + 1 ).toLowerCase();
			}
		}

		public FileInfo(String filePath) {
			this( new File( filePath ) );
		}

		/**
		 * @return 返回 扩展名ext。
		 */
		public String getExt() {
			return ext;
		}

		/**
		 * @return 返回 文件内容的md4的hash值。
		 */
		public synchronized String getHashCode() {
			if( hashCode == null && obj != null && obj.isFile() ) {
				hashCode = FileUtil.md4HashCode( obj );
			}
			return hashCode;
		}

		/**
		 * @return 返回 name。
		 */
		public String getName() {
			return name;
		}

		/**
		 * @return 返回 文件本身File对象。
		 */
		public File getObj() {
			return obj;
		}

		/**
		 * @return 返回 完整路径。
		 */
		public String getPath() {
			return path;
		}

		/**
		 * @return 返回 fileName，含扩展名。
		 */
		public String getFileName() {
			return fileName;
		}

	}

	/**
	 * 目录信息，如名称，子目录列表，目录中的文件列表
	 * 
	 */
	public static class Folder {
		private String name;

		private File obj;

		private List<File> children;

		private List<File> files;

		public Folder(File obj, FileFilter filter) {
			this.obj = obj;
			this.name = obj.getName();
			children = FileUtil.listDirectorys( obj, false );
			files = FileUtil.listAll( obj, filter, false );
		}

		/**
		 * @return 返回 子目录列表。
		 */
		public List<File> getChildren() {
			return children;
		}

		/**
		 * @param children 要设置的 子目录列表。
		 */
		public void setChildren( List<File> children ) {
			this.children = children;
		}

		/**
		 * @return 返回 目录中的文件列表。
		 */
		public List<File> getFiles() {
			return files;
		}

		/**
		 * @param files 要设置的 目录中的文件列表。
		 */
		public void setFiles( List<File> files ) {
			this.files = files;
		}

		/**
		 * @return 返回 目录名。
		 */
		public String getName() {
			return name;
		}

		/**
		 * @param name 要设置的 目录名。
		 */
		public void setName( String name ) {
			this.name = name;
		}

		/**
		 * @return 返回 目录本身对应的File对象。
		 */
		public File getObj() {
			return obj;
		}

		/**
		 * @param obj 要设置的 目录本身对应的File对象。
		 */
		public void setObj( File obj ) {
			this.obj = obj;
		}

	}

	/**
	 * 获取文件的类型，即扩展名
	 * 
	 * @param fileName 文件名
	 * @return 文件的类型，即扩展名
	 */
	public static String getFileType( String fileName ) {
		String ext = "";
		if( fileName != null && fileName.trim().length() > 0 ) {
			int endIndex = fileName.lastIndexOf( "." );
			if( endIndex > 0 ) {
				ext = fileName.substring( endIndex + 1 ).toLowerCase();
			}
		}
		return ext;
	}

	/**
	 * 执行一个可执行文件，含参数（命令行）
	 * 
	 * @param command 命令行
	 * @return 命令的标准输出流的输出信息
	 * @throws RuntimeException
	 */
	public static String execCommand( String command ) throws RuntimeException {
		Process process = null;
		InputStreamReader ir = null;
		InputStream in = null;
		LineNumberReader lnr = null;
		try {
			StringBuffer msg = new StringBuffer();
			process = Runtime.getRuntime().exec( command );

			in = process.getInputStream();
			ir = new InputStreamReader( in );
			lnr = new LineNumberReader( ir );
			String line = null;
			while( (line = lnr.readLine()) != null ) {
				// msg = new String(msg.getBytes("iso8859-1"), "GB18030");
				msg.append( line ).append( "\n" );
			}
			// in.close();
			// ir.close();
			// lnr.close();
			StringBuffer error = new StringBuffer();
			in = process.getErrorStream();
			ir = new InputStreamReader( in );
			lnr = new LineNumberReader( ir );
			while( (line = lnr.readLine()) != null ) {
				// error = new String(error.getBytes("iso8859-1"), "GBK");
				error.append( line ).append( "\n" );
				;
			}
			// in.close();
			// ir.close();
			// lnr.close();
			if( error.length() > 0 ) {
				throw new RuntimeException( error.toString() );
			}
			return msg.toString();

		}
		catch( IOException e ) {
			e.printStackTrace();
			throw new RuntimeException( e );
		}
		finally {
			close( process.getInputStream() );
			close( process.getErrorStream() );
			close( process.getOutputStream() );
			// if( process.waitFor() != 0 );
			// process.destroy();
		}

	}

	/**
	 * 对文件内容做md5的hash运算
	 * 
	 * @param fileName 文件路径
	 * @return md5的hash值
	 */
	public static String md5HashCode( String fileName ) {
		File file = new File( fileName );
		if( file.exists() ) {
			return md5HashCode( file );
		}
		else {
			return null;
		}
	}

	/**
	 * 对文件内容做md5的hash运算
	 * 
	 * @param sourceFile 文件对象
	 * @return md5的hash值
	 */
	public static String md5HashCode( File sourceFile ) {
		FileInputStream fis = null;
		MessageDigest md = null;
		try {
			md = MessageDigest.getInstance( "MD5" );
		}
		catch( NoSuchAlgorithmException ne ) {
			ne.printStackTrace();
		}
		try {
			fis = new FileInputStream( sourceFile );
			byte[] buffer = new byte[8192];
			int length;
			while( (length = fis.read( buffer )) != -1 ) {
				md.update( buffer, 0, length );
			}
			return StringUtil.toHex( md.digest() );

		}
		catch( FileNotFoundException e ) {
			return null;
		}
		catch( IOException e ) {
			return null;
		}
		finally {
			close( fis );
		}
	}

	/**
	 * 对文件内容做md4的hash运算
	 * 
	 * @param filename 文件名
	 * @return md4的hash值
	 */
	public static String md4HashCode( String filename ) {
		try {
			IMD4 md4 = MD4Factory.getInstance();
			return md4.addFile( filename );
		}
		catch( Exception e ) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 对文件内容做md4的hash运算
	 * 
	 * @param file 文件
	 * @return md4的hash值
	 */
	public static String md4HashCode( File file ) {
		try {
			IMD4 md4 = MD4Factory.getInstance();
			return md4.addFile( file );
		}
		catch( Exception e ) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 修改文件的最后访问时间。 如果文件不存在则创建该文件。
	 * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
	 * 
	 * @param file 需要修改最后访问时间的文件。
	 * @since 1.0
	 */
	public static boolean touch( File file ) {
		if( !file.exists() ) {
			try {
				return file.createNewFile();
			}
			catch( IOException e ) {
				e.printStackTrace();
			}
		}
		return file.setLastModified( System.currentTimeMillis() );
	}

	/**
	 * 修改文件的最后访问时间。 如果文件不存在则创建该文件。
	 * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
	 * 
	 * @param fileName 需要修改最后访问时间的文件的文件名。
	 * @since 1.0
	 */
	public static boolean touch( String fileName ) {
		File file = new File( fileName );
		return touch( file );
	}

	/**
	 * 修改文件的最后访问时间。 如果文件不存在则创建该文件。
	 * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
	 * 
	 * @param files 需要修改最后访问时间的文件数组。
	 * @since 1.0
	 */
	public static void touch( File[] files ) {
		for( int i = 0; i < files.length; i++ ) {
			touch( files[i] );
		}
	}

	/**
	 * 修改文件的最后访问时间。 如果文件不存在则创建该文件。
	 * <b>目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考虑中。</b>
	 * 
	 * @param fileNames 需要修改最后访问时间的文件名数组。
	 * @since 1.0
	 */
	public static void touch( String[] fileNames ) {
		File[] files = new File[fileNames.length];
		for( int i = 0; i < fileNames.length; i++ ) {
			files[i] = new File( fileNames[i] );
		}
		touch( files );
	}

	/**
	 * 将文件名中的特殊字符替换为“_”
	 * 
	 * @param fileName
	 * @return 转换后的文件名
	 */
	public static String unixRegFileName( String fileName ) {
		return fileName.replaceAll(
		    "[\\x20\\x21\\x23\\x24\\x25\\x26\\x27\\x28\\x29\\x2B\\x3B\\x3d\\x40\\x5b\\x5c\\x5d\\x5e\\x60\\x7b\\x7d\\x7e]", "_" );
	}

	/**
	 * 判断指定的文件是否存在。
	 * 
	 * @param fileName 要判断的文件的文件名
	 * @return 存在时返回true，否则返回false。
	 * @since 1.0
	 */
	public static boolean isExist( String fileName ) {
		return new File( fileName ).exists();
	}

	/**
	 * 创建指定的目录。 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。 <b>注意：可能会在返回false的时候创建部分父目录。</b>
	 * 
	 * @param file 要创建的目录
	 * @return 完全创建成功时返回true，否则返回false。
	 * @since 1.0
	 */
	public static boolean makeParentFolder( File file ) {
		File parent = file.getParentFile();
		if( parent != null && !parent.exists() ) {
			return parent.mkdirs();
		}
		return false;
	}

	/**
	 * 创建指定的目录。 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。 <b>注意：可能会在返回false的时候创建部分父目录。</b>
	 * 
	 * @param fileName 要创建的目录的目录名
	 * @return 完全创建成功时返回true，否则返回false。
	 * @since 1.0
	 */
	public static boolean makeParentFolder( String fileName ) {
		return makeParentFolder( new File( fileName ) );
	}

	/**
	 * 清空指定目录中的文件。 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。
	 * 另外这个方法不会迭代删除，即不会删除子目录及其内容。
	 * 
	 * @param directory 要清空的目录
	 * @return 目录下的所有文件都被成功删除时返回true，否则返回false.
	 * @since 1.0
	 */
	public static boolean emptyFolder( File directory ) {
		boolean result = false;
		File[] entries = directory.listFiles();
		for( int i = 0; i < entries.length; i++ ) {
			if( !entries[i].delete() ) {
				result = false;
			}
		}
		return result;
	}

	/**
	 * 清空指定目录中的文件。 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。
	 * 另外这个方法不会迭代删除，即不会删除子目录及其内容。
	 * 
	 * @param directoryName 要清空的目录的目录名
	 * @return 目录下的所有文件都被成功删除时返回true，否则返回false。
	 * @since 1.0
	 */
	public static boolean emptyFolder( String directoryName ) {
		File dir = new File( directoryName );
		return emptyFolder( dir );
	}

	/**
	 * 迭代删除指定目录及其中的所有文件＆子目录。
	 * 
	 * @param dirName 要删除的目录的目录名
	 * @param deleteAll 是否删除全部， 为true时迭代删除子目录的所有内容,
	 *            false时只删除当前目录中可以删除的内容,如果子目录的内容不会空时不能删除.
	 * @return 删除成功时返回true，否则返回false。
	 * @since 1.0
	 */
	public static boolean deleteDirectory( String dirName, boolean deleteAll ) throws IllegalArgumentException {
		return deleteDirectory( new File( dirName ), deleteAll );
	}

	/**
	 * 迭代删除指定目录及其中的所有文件＆子目录。
	 * 
	 * @param dir 要删除的目录
	 * @param deleteAll 是否删除全部，为true时迭代删除子目录的所有内容,
	 *            false时只删除当前目录中可以删除的内容,如果子目录的内容不会空时不能删除.
	 * @return 删除成功时返回true，否则返回false。
	 * @since 1.0
	 */
	public static boolean deleteDirectory( File dir, boolean deleteAll ) {
		if( (dir == null) || !dir.isDirectory() ) {
			return false;
		}

		File[] entries = dir.listFiles();
		int sz = entries.length;
		boolean result = true;

		for( int i = 0; i < sz; i++ ) {
			if( entries[i].isDirectory() && deleteAll ) {
				if( !deleteDirectory( entries[i], deleteAll ) )
					result = false;
			}
			if( !entries[i].delete() )
				result = false;
		}

		if( !dir.delete() ) {
			result = false;
		}
		return result;
	}

	/**
	 * 列出目录中的文件，可包括子目录中的内容。
	 * 
	 * @param dirName 要列出的目录
	 * @param listAll 是否递归查询
	 * @return 所有文件的列表
	 */
	public static List<File> listFiles( String dirName, boolean listAll ) {
		return listFiles( new File( dirName ), listAll );
	}

	/**
	 * 列出目录中的文件，可包括子目录中的内容。
	 * 
	 * @param dir 要列出的目录
	 * @param listAll 是否递归查询
	 * @return 所有文件列表
	 */
	public static List<File> listFiles( File dir, boolean listAll ) {
		return listAll( dir, new FileFilter() {
			public boolean accept( File f ) {
				return f.isFile();
			}
		}, listAll );
	}

	/**
	 * 列出目录中的子目录，可包括子目录中的所有子目录。
	 * 
	 * @param dirName 要列出的目录
	 * @param listAll 是否递归查询
	 * @return 所有子目录列表
	 */
	public static List<File> listDirectorys( String dirName, boolean listAll ) {
		return listDirectorys( new File( dirName ), listAll );
	}

	/**
	 * 列出目录中的子目录，可包括子目录中的所有子目录。
	 * 
	 * @param dir 要列出的目录
	 * @param listAll 是否递归查询
	 * @return File对象的列表
	 */
	public static List<File> listDirectorys( File dir, boolean listAll ) {
		return listAll( dir, new FileFilter() {
			public boolean accept( File f ) {
				return f.isDirectory();
			}
		}, listAll );
	}

	/**
	 * 列出目录中的子目录，可包括子目录中的所有子目录。
	 * 
	 * @param dirName 要列出的目录名称，全路径
	 * @param filter 文件过滤规则
	 * @return FileUtil.Folder对象的列表
	 */
	public static Map<String, Folder> getFolderMap( String dirName, FileFilter filter ) {
		return getFolderMap( new File( dirName ), filter );
	}

	/**
	 * 列出目录中的子目录，可包括子目录中的所有子目录。
	 * 
	 * @param dir 要列出的目录
	 * @param filter 文件过滤规则
	 * @return FileUtil.Folder对象的列表
	 */
	public static Map<String, Folder> getFolderMap( File dir, FileFilter filter ) {

		HashMap<String, Folder> map = new HashMap<String, Folder>();
		if( !dir.exists() ) {
			return map;
		}
		getFolderMap( map, dir, filter );
		return map;
	}

	private static void getFolderMap( Map<String, Folder> map, File dir, FileFilter filter ) {
		Folder folder = new Folder( dir, filter );
		map.put( dir.getAbsolutePath(), folder );
		Iterator<File> iter = folder.children.iterator();
		while( iter.hasNext() ) {
			File child = (File)iter.next();
			getFolderMap( map, child, filter );
		}
	}

	/**
	 * 列出目录中的所有文件＆目录列表，可包括其子目录中的内容。
	 * 
	 * @param file 要列出的目录
	 * @param filter 过滤器
	 * @param listAll 是否递归查询
	 * @return 目录内容的文件列表。
	 * @since 1.0
	 */
	public static List<File> listAll( File file, FileFilter filter, boolean listAll ) {
		ArrayList<File> list = new ArrayList<File>();
		if( !file.exists() ) {
			return list;
		}
		listAll( list, file, filter, listAll );
		if( list.contains( file ) ) {
			list.remove( file );
		}
		return list;
	}

	/**
	 * 列出目录中的所有文件＆目录列表，可包括其子目录中的内容。
	 * 
	 * @param list 文件列表
	 * @param filter 过滤器
	 * @param file 目录
	 * @param listAll 是否递归查询
	 */
	private static void listAll( ArrayList<File> list, File file, FileFilter filter, boolean listAll ) {
		if( file.isDirectory() ) {
			File files[] = file.listFiles();
			for( int i = 0; i < files.length; i++ ) {
				if( filter.accept( files[i] ) ) {
					list.add( files[i] );
				}
				if( listAll ) {
					listAll( list, files[i], filter, listAll );
				}
			}
		}

	}

	/**
	 * 将DOS/Windows格式的路径转换为UNIX/Linux格式的路径。
	 * 其实就是将路径中的"\"全部换为"/"，因为在某些情况下我们转换为这种方式比较方便，
	 * 某中程度上说"/"比"\"更适合作为路径分隔符，而且DOS/Windows也将它当作路径分隔符。
	 * 
	 * @param filePath 转换前的路径
	 * @return 转换后的路径
	 * @since 1.0
	 */
	public static String toUNIXpath( String filePath ) {
		return filePath.replace( '\\', '/' );
	}

	/**
	 * 拷贝文件。
	 * 
	 * @param fromFileName 源文件名
	 * @param toFileName 目标文件名
	 * @return 成功生成文件时返回true，否则返回false
	 * @since 1.0
	 */
	public static boolean copy( String fromFileName, String toFileName ) {
		return copy( fromFileName, toFileName, false );
	}

	/**
	 * 移动文件。
	 * 
	 * @param fromFileName 源文件名
	 * @param toFileName 目标文件名
	 * @return 成功生成文件时返回true，否则返回false
	 * @since 1.0
	 */
	public static boolean move( String fromFileName, String toFileName ) {
		File fromFile = new File( fromFileName );
		File toFile = new File( toFileName );
		return move( fromFile, toFile, false );
	}

	/**
	 * 移动文件。
	 * 
	 * @param fromFileName 源文件名
	 * @param toFileName 目标文件名
	 * @param override 目标文件存在时是否覆盖
	 * @return 成功生成文件时返回true，否则返回false
	 * @since 1.0
	 */
	public static boolean move( String fromFileName, String toFileName, boolean override ) {
		File fromFile = new File( fromFileName );
		File toFile = new File( toFileName );
		return move( fromFile, toFile, override );
	}

	/**
	 * 移动文件。
	 * 
	 * @param fromFile 源文件
	 * @param toFile 目标文件
	 * @param override 目标文件存在时是否覆盖
	 * @return 成功生成文件时返回true，否则返回false
	 * @since 1.0
	 */
	public static boolean move( File fromFile, File toFile, boolean override ) {
		boolean ret = copy( fromFile, toFile, override );
		if( ret )
			ret = fromFile.delete();
		return ret;
	}

	/**
	 * 拷贝文件。
	 * 
	 * @param fromFileName 源文件名
	 * @param toFileName 目标文件名
	 * @param override 目标文件存在时是否覆盖
	 * @return 成功生成文件时返回true，否则返回false
	 * @since 1.0
	 */
	public static boolean copy( String fromFileName, String toFileName, boolean override ) {
		File fromFile = new File( fromFileName );
		File toFile = new File( toFileName );
		return copy( fromFile, toFile, override );
	}

	/**
	 * 拷贝文件。
	 * 
	 * @param fromFile 源文件
	 * @param toFile 目标文件
	 * @param override 目标文件存在时是否覆盖
	 * @return 成功生成文件时返回true，否则返回false
	 * @since 1.0
	 */
	public static boolean copy( File fromFile, File toFile, boolean override ) {
		if( !fromFile.exists() || !fromFile.isFile() || !fromFile.canRead() ) {
			return false;
		}

		if( toFile.isDirectory() ) {
			toFile = new File( toFile, fromFile.getName() );

		}
		if( toFile.exists() ) {
			if( !toFile.canWrite() || override == false ) {
				return false;
			}
		}
		else {
			String parent = toFile.getParent();
			if( parent == null ) {
				parent = System.getProperty( "user.dir" );
			}
			File dir = new File( parent );
			if( !dir.exists() || dir.isFile() || !dir.canWrite() ) {
				return false;
			}
		}

		BufferedInputStream from = null;
		try {
			from = new BufferedInputStream( new FileInputStream( fromFile ) );
			writeToFile( from, toFile );
			return true;
		}
		catch( IOException e ) {
			e.printStackTrace();
			return false;
		}
		finally {
			close( from );
		}
	}

	/**
	 * 将指定文件的内容输出到指定的输出流中
	 * 
	 * @param fromFile 指定文件
	 * @param out 指定的输出流
	 * @return 如果成功放回true，否则返回false
	 * @throws IOException
	 */
	public static boolean putFileToStream( File fromFile, OutputStream out ) throws IOException {
		if( !fromFile.exists() || !fromFile.isFile() || !fromFile.canRead() ) {
			return false;
		}

		BufferedInputStream from = null;
		try {
			from = new BufferedInputStream( new FileInputStream( fromFile ) );
			pipeTransform( from, out );
			return true;
		}
		catch( IOException e ) {
			throw e;
		}
		finally {
			close( from );
		}
	}

	/**
	 * 将指定文本文件按指定的编码读出
	 * 
	 * @param srcFile 指定文本文件
	 * @param encoding 指定的编码
	 * @return 文本文件的内容
	 * @throws IOException
	 */
	public static String readTextFile( File srcFile, String encoding ) throws IOException {
		InputStreamReader from = null;
		try {
			char[] buffer = new char[4 * 1024];
			int read = 0;
			from = new InputStreamReader( new FileInputStream( srcFile ), encoding );
			StringBuffer result = new StringBuffer();
			while( (read = from.read( buffer, 0, buffer.length )) != -1 ) {
				result.append( buffer, 0, read );
			}
			return result.toString();
		}
		catch( IOException e ) {
			throw e;
		}
		finally {
			close( from );
		}
	}

	/**
	 * 从输入流中读取内容并写入到指定文件中
	 * 
	 * @param iStream 输入流
	 * @param fileName 指定输出文件名
	 * @throws IOException
	 */
	public static void writeToFile( InputStream iStream, String fileName ) throws IOException {
		String me = "FileUtil.WriteToFile";
		if( fileName == null ) {
			throw new IOException( me + ": filename is null" );
		}
		File theFile = new File( fileName );
		// Check if a file exists.
		if( theFile.exists() ) {
			String msg = theFile.isDirectory() ? "directory" : (!theFile.canWrite() ? "not writable" : null);
			if( msg != null ) {
				throw new IOException( me + ": file '" + fileName + "' is " + msg );
			}
		}
		writeToFile( iStream, theFile );
	}

	/**
	 * 从输入流中读取内容并写入到指定文件中
	 * 
	 * @param iStream 输入流
	 * @param theFile 指定输出文件
	 * @throws IOException
	 */
	public static void writeToFile( InputStream iStream, File theFile ) throws IOException {
		String me = "FileUtil.WriteToFile";
		if( theFile == null ) {
			throw new IOException( me + ": theFile is null" );
		}
		if( iStream == null ) {
			throw new IOException( me + ": InputStream is null" );
		}
		// Save InputStream to the file.
		FileOutputStream fOut = null;
		try {
			fOut = new FileOutputStream( theFile );
			pipeTransform( iStream, fOut );
		}
		catch( Exception e ) {
			throw new IOException( me + " failed, got: " + e.toString() );
		}
		finally {
			close( iStream );
			close( fOut );
		}
	}

	public static boolean writeToTextFile( String fileName, String content, String encoding ) {
		OutputStreamWriter writer = null;
		try {
			writer = new OutputStreamWriter( new FileOutputStream( fileName ), encoding );
			writer.write( content );
			return true;
		}
		catch( Exception e ) {
			e.printStackTrace();
		}
		finally {
			close( writer );
		}
		return false;
	}

	/**
	 * 字节流管道传输，从输入流中读取字节，并输出到输出流中
	 * 
	 * @param iStream
	 * @param oStream
	 * @throws IOException
	 */
	public static void pipeTransform( InputStream iStream, OutputStream oStream ) throws IOException {
		byte[] buffer = new byte[1 * 1024];
		int bytesRead = 0;
		while( (bytesRead = iStream.read( buffer, 0, buffer.length )) != -1 ) {
			oStream.write( buffer, 0, bytesRead );
		}
	}

	public static void pipeTransform( Reader reader, Writer writer ) throws IOException {
		char[] buffer = new char[4 * 1024];
		int read = 0;
		while( (read = reader.read( buffer, 0, buffer.length )) != -1 ) {
			writer.write( buffer, 0, read );
		}
	}

	public static void convertEncoding( String srcFileName, String dstFileName, String srcEncoding, String dstEncoding )
	        throws IOException {
		convertEncoding( new File( srcFileName ), new File( dstFileName ), srcEncoding, dstEncoding );
	}

	public static void convertEncoding( File srcFile, File dstFile, String srcEncoding, String dstEncoding ) throws IOException {
		FileUtil.makeParentFolder( dstFile );

		InputStreamReader from = null;
		Writer out = null;
		try {
			out = new OutputStreamWriter( new FileOutputStream( dstFile ), dstEncoding );
			from = new InputStreamReader( new FileInputStream( srcFile ), srcEncoding );
			pipeTransform( from, out );
		}
		finally {
			close( from );
			close( out );
		}
	}

	public static void close( Closeable closeable ) {
		if( closeable != null ) {
			try {
				closeable.close();
			}
			catch( Exception e ) {
				e.printStackTrace();
			}
		}
	}

	public static void main( String[] args ) throws Exception {

	}

}
